export function createElement(type: string | (new () => VElement), attributes: Record<string, string>, ...children: VComponent[]): VComponent {
  let element: VElement;

  if (typeof type === 'string') {
    element = new VElement(type);
  } else {
    element = new type();
  }

  for (const attribute in attributes) {
    element.setAttribute(attribute, attributes[attribute]);
  }

  let processChildren = (children) => {
    for (let child of children) {
      if (typeof child === 'string') {
        child = new VText(child);
      }

      if (Array.isArray(child)) {
        processChildren(child);
        continue;
      }
  
      element.appendChild(child);
    }  
  };

  processChildren(children);

  return element;
}

(window as any).createElement = createElement;

// Virtual Dom
export abstract class VComponent {
  protected root: Node | null = null;

  protected state = Object.create(null);
  protected attributes = Object.create(null);

  mountTo(parent: Node) {
    if (this.root == null) {
      this.render();
    }

    parent.appendChild(this.root!);
  }

  setAttribute(name: string, value: any) {
    this.attributes[name] = value;
  }

  appendChild(child: VComponent) {
    if (this.root == null) {
      return;
    }

    child.mountTo(this.root);
  }

  render() {
    return this.root;
  }

  triggerEvent(type: string, args: any) {
    this.attributes['on' + type.replace(/^[\s\S]/, s => s.toUpperCase())](new CustomEvent(type, { detail: args }));
  }
}

export class VElement extends VComponent {
  protected root: HTMLElement;

  constructor(type: string) {
    super();
    this.root = document.createElement(type);
  }

  setAttribute(name: string, value: string) {
    this.root.setAttribute(name, value);
  }
}

// Html Text Node
export class VText extends VComponent {
  protected root: Text;
  
  constructor(data: string) {
    super();
    this.root = document.createTextNode(data);
  }
}